package wili.exp.simple;

import java.util.List;
import java.util.Map;

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;

import wili.c.Ca;
import wili.exp.simple.antlr.opExpLexer;
import wili.exp.simple.antlr.opExpParser;
import wili.exp.simple.antlr.opExpParser.OpExpContext;

/**
 * 
 * @author weilai 2022-12-12
 */
public abstract class Sexp {
	public final int id = ++GenID;
	public String name;
	public String op;
	public List<Sexp> sons;

	static interface C {
		String ExpContext = "ExpContext", Exp3Context = "Exp3Context";
		String OpExpContext = "OpExpContext", OpExp3Context = "OpExp3Context";
		String VarContext = "VarContext";
		String OpContext = "OpContext";
		String FunContext = "FunContext";
		String ArgsContext = "ArgsContext";
		String DnameContext = "DnameContext";
		String NumContext = "NumContext";
	}

	public Sexp left() {
		return sons.get(0);
	}

	public abstract String toNumString(Map cont);

	public Sexp right() {
		return sons.get(1);
	}

	public Sexp(Object o) {
		if (id == 8)
			Ca.pause();
	}

	public static Sexp newInst(String exprSt) {
		if ("asdf".isEmpty()) {
			exprSt = exprSt.replaceAll("(\\w+)(\\d+)", "$1^$2");
		}
		opExpParser pa = newSexpParser(exprSt);
		OpExpContext eCont = pa.opExp();
		if (eCont.exception != null) {
			eCont.exception.printStackTrace();
			return null;
		}
		Node node = new Node(eCont, "OpContext");
		if (node.sons.size() == 1) {
			node = node.sons.get(0);
		}
		node.standardizeByOpPriority();
		return newInst(node);
	}

	static Sexp newInst(Node node) {
		if (node.isOp) {
			return new Sop(node);
		}
		switch (node.clsNameOrOp) {
		case C.DnameContext:
			break;
		case C.ExpContext:
			//sons.add(new Sexp(nodSon));
			if (node.sons.size() == 1) {
				node = node.sons.get(0);
				String st = node.clsNameOrOp;
				if (st.equals(C.VarContext)) {
					if (node.tree.getText().matches("true|false|Y|N")) {
						return new Svalue(node);
					}
					return new Svar(node);
				} else if (st.equals(C.NumContext)) {
					return new Svalue(node);
				} else if (st.equals(C.FunContext)) {
					return new Sfun(node);
				} else {
					Ca.asert(false, st);
				}
			} else if (node.sons.size() == 3) {
			}
			Ca.pause();
		case C.OpExp3Context: {
			return newInst(node.sons.get(1));
		}
		case C.OpContext:
			return new Sop(node);
		case C.VarContext:
			return new Svar(node);
		case C.FunContext:
			return new Sfun(node);
		case C.ArgsContext:
			break;
		case C.NumContext:
			return new Svalue(node);
		default:
			break;
		}
		return null;
	}

	@SuppressWarnings("rawtypes")
	public abstract Boolean isTrue(Map cont);

	/**
	 * @param <T>
	 * @param cont
	 * @return number or boolean.
	 */
	public abstract <T> T value(Map cont);

	static opExpParser newSexpParser(String exprSt) {
		CharStream cs = CharStreams.fromString(exprSt);
		opExpLexer lexer = new opExpLexer(cs);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		return new opExpParser(tokens);
	}

	public static void test() {
		Sexp_test.basicTest(1);
	}

	static int GenID;
}
